
/****************************************************************************/
/*Copyright (c) 2011, Florent DEVILLE.                                      */
/*All rights reserved.                                                      */
/*                                                                          */
/*Redistribution and use in source and binary forms, with or without        */
/*modification, are permitted provided that the following conditions        */
/*are met:                                                                  */
/*                                                                          */
/* - Redistributions of source code must retain the above copyright         */
/*notice, this list of conditions and the following disclaimer.             */
/* - Redistributions in binary form must reproduce the above                */
/*copyright notice, this list of conditions and the following               */
/*disclaimer in the documentation and/or other materials provided           */
/*with the distribution.                                                    */
/* - The names of its contributors cannot be used to endorse or promote     */
/*products derived from this software without specific prior written        */
/*permission.                                                               */
/* - The source code cannot be used for commercial purposes without         */ 
/*its contributors' permission.                                             */
/*                                                                          */
/*THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS       */
/*"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT         */
/*LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS         */
/*FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE            */
/*COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,       */
/*INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,      */
/*BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;          */
/*LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER          */
/*CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT        */
/*LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN         */
/*ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE           */
/*POSSIBILITY OF SUCH DAMAGE.                                               */
/****************************************************************************/

#pragma once

//QWidget class
//#include <QGLWidget>
#include <QtOpenGL\qgl.h>

//contextual menu class
class GRTContextualMenu;

//forward declaration
class QTimer;
class QKeyEvent;
class QWheelEvent;
class QMouseEvent;

//vector class
#include "RTVector3f.h"
using RT::RTVector3f;

class GRTMovable;
class GRTGeometry;
class GRTILight;
class GRTICamera;

#include "GRTMemoryLeak.h"

enum DISPLACEMENT_MODE
{
	D_TRANSLATE,
	D_ROTATE,
	D_SCALE,
	D_NONE
};

#include "GRTGeometryType.h"
#include "GRTLightType.h"

//This class is used to display an opengl scene.
class GRTViewport : public QGLWidget
{
	Q_OBJECT
private:
	//time to refresh the scene
	QTimer* _timer;
	
	//camera lookat position
	RTVector3f _eye;

	//camera lookat at (scene center)
	RTVector3f _at;

	//camera up vector
	RTVector3f _up;

	//previous position of mouse
	RTVector3f _mousePreviousPosition;

	//position of the click
	RTVector3f _mouseClickPosition;

	//flag indicates if the camera has to rotate
	bool _bRotationCamera;

	//flag indicates if we are moving an object
	bool _bTranslation;

	//flag indicates if we are doing a rotation to an object
	bool _bRotation;

	//flag indicates if we are scaling an object
	bool _bScale;

	//flag indicates the add of a primitive to the scene
	bool _bAdd;
	GEOMETRYTYPE _typeToAdd;
	LIGHTTYPE _lightToAdd;

	//each element is the rotation angle of the camera
	RTVector3f _orientationCamera;

	//right click contextual menu
	GRTContextualMenu* _rightClickMenu;

	//wireframe flag
	bool _wireframe;

	//viewport vector
	GLint _viewport[4];

	//model view matrix
	GLdouble _modelview[16];

	//prjection matrix
	GLdouble _projection[16];

	//flag indicates if wr draw the translation mark
	DISPLACEMENT_MODE _displacementMode;

	bool _cameraGRTScene;

	//indicating what type of mesh to add
	MESHTYPE _typeOfMeshToAdd;

public:

	//constructor with shared widget (opengl context)
	GRTViewport(QWidget* parent=0, QGLWidget* shareWidget=0);

	//destructor
	virtual ~GRTViewport(void);

	//new operator (16 bites aligned if one day i'm brave enough to use SSE)
	//void* operator new(size_t size);

	//delete operator
	//void operator delete(void* p);

protected:

	//initialise open gl
	virtual void initializeGL(void);

	//resize the viewport
	virtual void resizeGL(int width, int height);

	//draw
	virtual void paintGL(void);

	//handle key input
	virtual void keyPressEvent(QKeyEvent* keyEvent );

	//handle mouse wheel
	virtual void wheelEvent(QWheelEvent* wheelEvent);

	//handle mouse movements
	virtual void mouseMoveEvent(QMouseEvent* mouseEvent);

	//handle mouse click
	virtual void mousePressEvent(QMouseEvent* mouseEvent);

	//handle click release
	virtual void mouseReleaseEvent(QMouseEvent* mouseEvent);

	//handle double click
	virtual void mouseDoubleClickEvent (QMouseEvent* mouseEvent);

public slots:
	//time out slots
	void timeOutSlot();

	//affiche un menu contextuel
	void menuContextuel(const QPoint& iPoint);

	//set the camera position and orientation for a front view
	void setCameraFrontView();

	//set the camera position and orientation for a back view
	void setCameraBackView();

	//set the camera position and orientation for a left view
	void setCameraLeftView();

	//set the camera position and orientation for a right view
	void setCameraRightView();

	//set the camera to the scene camera
	void setCameraGRTSceneView();

	//change wireframe to solid or solid to wireframe
	void toggleWireframe();

	//select an object in the scene
	void selectObject(int x, int y);

	//activate the translation mode
	void toggleTranslationMode();

	//toggle the rotation mode
	void toggleRotationMode();

	//toggle the scale mode
	void toggleScaleMode();

	//toggle the add primitive mode for sphere
	void toggleAddModeSphere();
	
	//toggle the add primitive mode for box
	void toggleAddModeBox();

	//toggle the add primitive mode for cylinder
	void toggleAddModeCylinder();

	//toggle the add primitive mode for cone
	void toggleAddModeCone();

	//toggle the add primitive mode for torus
	void toggleAddModeTorus();

	//toggle the add primitive mode for cube mesh
	void toggleAddModeCubeMesh();

	//toggle the add primitive mode for plan mesh
	void toggleAddModePlanMesh();

	//deactivate the add mode
	void unsetAddMode();

	//toggle the add light mode for directional
	void toggleAddModeGRTDirectionalLight();

	//toggle the add light mode for point light
	void toggleAddModeGRTPointLight();

	//toggle the add light mode for spot light
	void toggleAddModeGRTSpotLight();

	//refresh the camera of the scene if the viewport is in camera scene mode
	void refreshCameraGRTScene();

signals:
	//signal indicating that an object has been selected
	void objectGRTGeometrySelected(GRTGeometry* primitive);

	//signal sent when nothing is selected
	void objectGRTMovableUnselected(GRTMovable* movable);

	//signal sent when an object is unselected
	void objectUnselected(GRTGeometry* primitive);

	//signal sent when the light is selected
	void objectLightSelected(GRTILight* light);

	//signal sent when the camera is selected
	void objectCameraSelected(GRTICamera* cam);

	//signal sent when the selected object has is position changed
	void objectMoved(float x, float y, float z);

	//signal sent when the selected object has been rotated
	void objectRotated(float x, float y, float z);

	//signal sent when the selected object has been scaled
	void objectScaled(float x, float y, float z);

	//signal sent when an object has been added.
	void objectAdded();

	//signal sent when the scale mode is unavailable and unset itself
	void unsetScaleMode();

	//signal sent when the camera scene is moved
	void cameraGRTSceneMoved();

	//signal sent when an object is selected
	void objectGRTMovableSelected(GRTMovable* object);

private:

	//apply wireframe mode
	void applyWireframe();

	//gives world coordinates (wx, wy, wz) of a clicked point (sx, sy)
	void screenToWorld(int sx, int sy, float& wx, float& wy, float& wz)const;

	//rotate the camera
	void rotateCamera(int x, int y);

	//translate the selected object
	void translateSelectedObject(int x, int y);

	//rotate the selected object
	void rotateSelectedObject(int x, int y);

	//scale the selected object
	void scaleSelectedObject(int x, int y);

	//add a primitive to the scene
	void addPrimitive();

	//add a lighht to the scene
	void addLight();

	//calculate mark of the camera
	void getCameraMark(RTVector3f& right, RTVector3f& up, RTVector3f& front)const;

};
